package main

import (
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
)

/**
 * @Author:      YaoWang
 * @DateTime:    2017-04-21 13:47:28
 * @Description: Ftp结构体 包含参数 con连接 ip地址
 */
type Ftp struct {
	con net.Conn
	ip  string
}

func main() {
	ftp, err := Ftp.NewFtp("127.0.0.1:8080")
	ftp.Login("user", "pwd")

}

/**
 * @Author:      YaoWang
 * @DateTime:    2017-04-21 13:48:19
 * @Description: 创建Ftp整个对象并返回
 */
func NewFtp(ip string) (*Ftp, error) {
	//缓存 大小 buf 1024
	buf := make([]byte, 1024)
	//tcp连接 ip
	con, err := net.Dial("tcp", ip)
	if err != nil {
		return nil, err
	}
	//从链接通道读取数据 复制到buf上 n是数据的长度
	n, err := con.Read(buf)
	if err != nil {
		return nil, err
	}
	//打印所有数据
	fmt.Println(string(buf[:n]))
	return &Ftp{con, ip}, nil
}

/**
 * @Author:      YaoWang
 * @DateTime:    2017-04-21 13:51:28
 * @Description: 登陆操作
 */
func (self *Ftp) Login(user, pass string) error {
	buf := make([]byte, 1024)
	self.con.Write([]byte(fmt.Sprintf("USER %s\r\n", user)))
	self.con.Read(buf)
	self.con.Write([]byte(fmt.Sprintf("PASS %s\r\n", pass)))
	n, err := self.con.Read(buf)
	if err != nil {
		return err
	}
	if !strings.Contains(string(buf[:n]), "230 Logged on") {
		return errors.New(strings.TrimSpace(string(buf)))
	}
	return nil
}

/**
 * @Author:      YaoWang
 * @DateTime:    2017-04-21 13:52:17
 * @Description: PutPasv实现ftp的方法 PathName路径地址 貌似是复制文件地址
 */
func (self *Ftp) PutPasv(Pathname string) error {
	con, err := self.connection("STOR", Pathname)
	if err != nil {
		return err
	}
	File, err := os.Open(Pathname)
	if err != nil {
		con.Close()
		return err
	}
	io.Copy(con, File)
	File.Close()
	con.Close()
	buf := make([]byte, 1024)
	_, err = self.con.Read(buf)
	if err != nil {
		return err
	}
	return nil
}

/**
 * @Author:      YaoWang
 * @DateTime:    2017-04-21 13:54:16
 * @Description: 获取文件 Pathname
 */
func (self *Ftp) GetFile(Pathname string) error {
	con, err := self.connection("RETR", Pathname)
	if err != nil {
		return err
	}
	File, err := os.Create(Pathname)
	if err != nil {
		con.Close()
		return err
	}
	io.Copy(File, con)
	File.Close()
	con.Close()
	buf := make([]byte, 1024)
	_, err = self.con.Read(buf)
	if err != nil {
		return err
	}
	return nil
}

/**
 * @Author:      YaoWang
 * @DateTime:    2017-04-21 13:54:55
 * @Description: 连接
 */
func (self *Ftp) connection(status, Pathname string) (net.Conn, error) {
	buf := make([]byte, 1024)
	self.con.Write([]byte("PASV \r\n"))
	n, err := self.con.Read(buf)
	if err != nil {
		return nil, err
	}
	if s := string(buf[:n]); !strings.Contains(s, "227 Entering Passive Mode") {
		return nil, errors.New(s)
	}
	port := getport(buf[27 : n-3])
	con, err := net.Dial("tcp", fmt.Sprintf("%s:%d", strings.Split(self.ip, ":")[0], port))
	if err != nil {
		return nil, err
	}
	self.con.Write([]byte(fmt.Sprintf("%s %s\r\n", status, Pathname)))
	n, err = self.con.Read(buf)
	if err != nil {
		con.Close()
		return nil, err
	}
	if !strings.Contains(string(buf[:n]), "150 Opening data channel") {
		con.Close()
		return nil, errors.New("create data link error.")
	}
	return con, nil
}

/**
 * @Author:      YaoWang
 * @DateTime:    2017-04-21 13:55:27
 * @Description: 获取端口
 */
func getport(by []byte) int {
	s := string(by)
	list := strings.Split(s, ",")
	n1, err := strconv.Atoi(list[len(list)-2])
	if err != nil {
		return 0
	}
	n2, err := strconv.Atoi(list[len(list)-1])
	if err != nil {
		return 0
	}
	return n1*256 + n2
}
